#include "StdAfx.h"
#include "MyModule.h"
#include "diagnostics.h"
#include "resource.h"
#include <CommDlg.h>
using namespace std;
using namespace ATL;

//[emitidl];

// The module attribute is specified in order to implement DllMain,
// DllRegisterServer and DllUnregisterServer
[ 
	module(dll
	, name = "SharpDevelopSlnIconHandler"
	, helpstring = "SharpDevelop SLN IconHandler"
	, version = "1.0"
	, uuid = "DCF8E160-3977-4ABD-91EB-41F5866FFAE6" //This uuid will be used to register the TLB file
	) 
]
class  MyModule
{
	//HRESULT DllCanUnloadNow() throw()
public:

	MyModule()
		: _hModule(NULL)
		, _shell(__uuidof(WshShell))
	{
		getModuleHandle();
		getExecutableFileName();
	}

	~MyModule()
	{
		doCleanUp();
	}

	BOOL WINAPI DllMain(DWORD dwReason, LPVOID lpReserved) 
	{
		//Get the handle to the executable which has loaded this DLL

		dbgPrint(_T("MyModule::DllMain(reason=%d)\n"), dwReason);
		dbgPrint(_T("Path:%s\n"), _modulePath.c_str());

		/*if (dwReason == DLL_PROCESS_DETACH)
		{
		doCleanUp();
		}*/

		// add your own code here
		return __super::DllMain(dwReason, lpReserved);
	}

	HRESULT DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
	{
		dbgPrint(_T("MyModule::DllGetClassObject(rclsid=%s, riid=%s\n"), guidToString(rclsid).c_str(), guidToString(riid).c_str());

		HRESULT hr = __super::DllGetClassObject(rclsid, riid, ppv);

		if(FAILED(hr))
			dbgPrint(_T("DllGetClassObjectFailed! (hr=0x%08X)\n"), hr);

		return hr;
	}

	HRESULT DllRegisterServer(BOOL bRegTypeLib = TRUE)
	{
		savePreviousHandlerInfo();
		registerIconHandler();
		registerLauncher();
		registerSharpDev();
		return __super::DllRegisterServer(bRegTypeLib);
	}

	HRESULT DllUnregisterServer(BOOL bUnRegTypeLib = TRUE)
	{
		unregisterSharpDev();
		unregisterLauncher();
		unregisterIconHandler();
		restorePreviousHandler();
		return __super::DllUnregisterServer(bUnRegTypeLib);
	}

private:

	//The smart pointers are throwing exceptions during destruction hence
	//they are not being held as a member variables.
	//CComPtr<IRegistrar> _registrar;
	IWshShellPtr _shell;
	HMODULE	_hModule;
	_tstring _modulePath;
	_tstring _pathToSharpDevelop;
private:

	void doCleanUp()
	{
		//_shell = NULL;
		_shell.Detach();
	}

	void getExecutableFileName() 
	{
		if(_hModule == NULL)
			return;

		TCHAR buff[2048];
		GetModuleFileName(_hModule, buff, sizeof(buff));
		_modulePath = buff;
	}

	void getModuleHandle()
	{
		void* localAddr = &_hModule;//An address located within this module
		GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)&_hModule, &_hModule);
	}

	void registerSharpDev()
	{
		try
		{
			_bstr_t path = (_tstring(_T("\"")) + _pathToSharpDevelop + _tstring(_T("\" \"%1\""))).c_str();
			_bstr_t key = _T("HKCR\\SharpDevelop.sln\\Shell\\Open\\Command\\");
			_variant_t val = path;
			_shell->RegWrite(key, &val);
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not register SharpDevelop.sln. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}

	void unregisterSharpDev()
	{
		try
		{
			_shell->RegDelete(_T("HKCR\\SharpDevelop.sln\\Shell\\Open\\Command\\"));
			_shell->RegDelete(_T("HKCR\\SharpDevelop.sln\\Shell\\Open\\"));
			_shell->RegDelete(_T("HKCR\\SharpDevelop.sln\\Shell\\"));
			_shell->RegDelete(_T("HKCR\\SharpDevelop.sln\\"));
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not unregister SharpDevelop.sln. %s\n")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}


	void savePreviousHandlerInfo()
	{
		try
		{
			_bstr_t prevLauncher = _shell->RegRead(_T("HKCR\\") SLN_EXTENSION _T("\\"));

			//There's no need to save the prev handler if its Sharp launcher itself
			if (_bstr_t(_T("SharpDevelop.Launcher")) != prevLauncher)
			{
				_variant_t val = prevLauncher;
				_shell->RegWrite(APP_REG_KEY _T("\\prevLauncher\\"), &val);
			}
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not retrieve previous application launcher's information. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}

	void restorePreviousHandler()
	{
		try
		{
			_variant_t prevLauncher = _shell->RegRead(APP_REG_KEY _T("\\prevLauncher\\"));
			_shell->RegWrite(_T("HKCR\\") SLN_EXTENSION _T("\\"), &prevLauncher);
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not retrieve previous application launcher's information. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}

	void registerIconHandler()
	{
		try
		{
			//HKCR\\.sln\\ = 'SharpDevelop.Launcher'
			_variant_t value = _T("SharpDevelop.Launcher");
			_shell->RegWrite(_T("HKCR\\") SLN_EXTENSION _T("\\"), &value);

			//HKCR\\SharpDevelop.Launcher\\ = 'Sharp Launcher'
			value = _T("Sharp Launcher");
			_shell->RegWrite(_T("HKCR\\SharpDevelop.Launcher\\"), &value);

			//HKCR\\SharpDevelop.Launcher\\ShellEx\\IconHandler\\ = '{23020D1D-7050-4694-B6C7-DA5563C49955}'
			value = _T("{23020D1D-7050-4694-B6C7-DA5563C49955}");
			_shell->RegWrite(_T("HKCR\\SharpDevelop.Launcher\\ShellEx\\IconHandler\\"), &value);
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not register icon handler. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}	

	void unregisterIconHandler()
	{
		try
		{
			//HKCR\\SharpDevelop.Launcher
			_shell->RegDelete(_T("HKCR\\SharpDevelop.Launcher\\ShellEx\\IconHandler\\"));
			_shell->RegDelete(_T("HKCR\\SharpDevelop.Launcher\\ShellEx\\"));
			_shell->RegDelete(_T("HKCR\\SharpDevelop.Launcher\\"));
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not unregister icon handler. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}

	void locateSharpDevelop()
	{
		//Query registry key HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\SharpDevelop.exe
		//to find the path to sharpdevelop.exe

		try
		{
			_variant_t retVal = _shell->RegRead(_T("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\SharpDevelop.exe\\"));
			_pathToSharpDevelop = _bstr_t(retVal);
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not read sharpdevelop.exe app path from the registry. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
			//Do not re-throw
		}
	}

	void promptForSharpDevelopPath()
	{
		int retVal = MessageBox(NULL, _T("Could not locate SharpDevelop would you like to browse for it?"), _T("SharpDevelop"), MB_YESNO);

		if((retVal & MB_DEFBUTTON1) != MB_DEFBUTTON1)
			throw _texception(_T("User cancellation."));

		TCHAR fileName[MAX_PATH];
		_tcscpy(fileName, _T("SharpDevelop.exe"));
		OPENFILENAME ofn;
		memset(&ofn, 0, sizeof(ofn));
		ofn.lStructSize = sizeof(ofn);
		ofn.lpstrFilter = _T("Programs (*.exe)\0*.exe;\0");
		ofn.lpstrFile = fileName;
		ofn.nMaxFile = sizeof(fileName);
		ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;

		if(retVal != GetOpenFileName(&ofn))
			throw _texception(_T("User cancellation."));

		_pathToSharpDevelop = fileName;

		if(_pathToSharpDevelop == _T(""))
			throw _texception(_T("Bad file name."));
	}

	void saveSharpDevelopPath()
	{
		try
		{
			_variant_t val = _pathToSharpDevelop.c_str();
			_shell->RegWrite(APP_REG_KEY _T("\\PathToSharpDevelop"), &val);
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not save the path to sharpdevelop. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}

	void registerLauncher()
	{
		//The shell\Command key will be registered by SharpLauncher.exe itself
		//Simply save the path to SharpDevelop here
		locateSharpDevelop();
		if(_pathToSharpDevelop == _T(""))
			promptForSharpDevelopPath();
		//We're here so no exception was thrown
		saveSharpDevelopPath();
	}

	void unregisterLauncher()
	{
		//Delete the application key from the registry

		try
		{
			_shell->RegDelete(APP_REG_KEY _T("\\prevLauncher\\"));
			_shell->RegDelete(APP_REG_KEY _T("\\PathToSharpDevelop"));
			_shell->RegDelete(APP_REG_KEY _T("\\"));
		}
		catch (_com_error err)
		{
			dbgPrint(_T("Error(0x%X): Could not unregister launcher. %s")
				, err.Error()
				, _tstring(err.ErrorMessage()).c_str()
				);
		}
	}
};